﻿//-------------------------------------------------------------------------------------------------
// <copyright file="AutoCompleteComboBox.cs" company="Anthony Adam">
// Copyright (c) Anthony Adam 2012. All rights reserved.
// </copyright>
//-------------------------------------------------------------------------------------------------
// ReSharper disable once CheckNamespace
namespace SuperStarRounds
{
  using System;
  using System.ComponentModel;
  using System.Windows.Forms;

  /// <summary>
  /// Provides select as you type functionality to a ComboBox
  /// </summary>
  public class AutoCompleteComboBox : ComboBox
  {
    /// <summary>
    /// Flag indicating if the combo box is actively being edited.
    /// </summary>
    private bool inEditMode;

    /// <summary>
    /// Allows developers to decide on special actions when item entered is not in the list
    /// but generally, we ignore them to allow new items not thought of to be entered in this
    /// application.
    /// </summary>
    public event CancelEventHandler NotInList;
    
    /// <summary>
    /// Gets or sets a value indicating whether the control triggers or not in list events for 
    /// manually typed values
    /// </summary>
    [Category("Behaviour")]
    public bool LimitToList { get; set; } = true;

    /// <summary>
    /// If entered value is not in the choices for this instance, raise the not in list event
    /// for the developer to catch and handle.
    /// </summary>
    /// <param name="e">The event arguments</param>
    protected virtual void OnNotInList(CancelEventArgs e)
    {
      // if a handler for NotInList event is defined, call it
      NotInList?.Invoke(this, e);
    }

    /// <summary>
    /// Handle the On Text Changed standard event to check for search and select if we are
    /// manually editing the control value. Base functionality is called once this is checked
    /// and dealt with.
    /// </summary>
    /// <param name="e">The event arguments</param>
    protected override void OnTextChanged(EventArgs e)
    {
      if (inEditMode) 
      {
        var input = Text;
        var index = FindString(input);

        if (index >= 0)
        {
          inEditMode = false; // stop infinite recursion :)
          SelectedIndex = index;
          inEditMode = true;
          Select(input.Length, Text.Length);
        }
      }

      base.OnTextChanged(e);
    }

    /// <summary>
    /// If the control is validating, add the check for existence of current item in the
    /// selection list and raise the events as appropriate.
    /// </summary>
    /// <param name="e">The event arguments</param>
    protected override void OnValidating(CancelEventArgs e)
    {
      if (LimitToList) 
      {
        var pos = FindStringExact(Text);

        if (pos < 0) OnNotInList(e);
        else SelectedIndex = pos;
      }

      base.OnValidating(e);
    }

    /// <summary>
    /// Watch for editing within the control display zone to set the in edit mode appropriately.
    /// </summary>
    /// <param name="e">The event arguments</param>
    protected override void OnKeyDown(KeyEventArgs e)
    {
      inEditMode = e.KeyCode != Keys.Back && e.KeyCode != Keys.Delete;

      base.OnKeyDown(e);
    }
  }
}
